package com.bolt.common.reflect;

import com.bolt.common.lang.Assert;
import com.bolt.common.utils.ArrayUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by Administrator on 2017/8/11.
 */
public class AnnotationUtil {

    /**
     * 获取一个方法的所有注解
     *
     * @param method
     * @return
     */
    public static Annotation[] getAnnotations(Method method) {
        return method.getAnnotations();
    }


    /**
     * 查找一个类的某种注解类型
     *
     * @param clazz
     * @param annotationType
     * @return
     */
    public static Annotation findAnnotation(Class clazz, Class annotationType) {
        Annotation annotation = clazz.getAnnotation(annotationType);
        if (annotation != null)
            return annotation;
        Class clazzes[] = clazz.getInterfaces();
        int len = clazzes.length;
        for (int i = 0; i < len; i++) {
            Class ifc = clazzes[i];
            annotation = findAnnotation(ifc, annotationType);
            if (annotation != null)
                return annotation;
        }

        if (clazz.getSuperclass() == null || Object.class.equals(clazz.getSuperclass()))
            return null;
        else
            return findAnnotation(clazz.getSuperclass(), annotationType);
    }

    /**
     * 查找包含某种注解类型的Class类型的Class
     *
     * @param annotationType
     * @param clazz
     * @return
     */
    public static Class findAnnotationDeclaringClass(Class annotationType, Class clazz) {
        if (clazz == null || clazz.equals(Object.class))
            return null;
        else
            return isAnnotationDeclaredLocally(annotationType, clazz) ? clazz : findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
    }

    /**
     * 检查一个类是否包含一个特定的注解类型
     *
     * @param annotationType
     * @param clazz
     * @return
     */
    public static boolean isAnnotationDeclaredLocally(Class clazz, Class annotationType) {
        boolean declaredLocally = false;
        Iterator i$ = Arrays.asList(clazz.getDeclaredAnnotations()).iterator();
        do {
            if (!i$.hasNext())
                break;
            Annotation annotation = (Annotation) i$.next();
            if (!annotation.annotationType().equals(annotationType))
                continue;
            declaredLocally = true;
            break;
        } while (true);
        return declaredLocally;
    }

    public static boolean isAnnotationInherited(Class annotationType, Class clazz) {
        return clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz);
    }

    /**
     * 获取注解所有的属性
     *
     * @param annotation
     * @return
     */
    public static Map getAnnotationAttributes(Annotation annotation) {
        Map attrs = new HashMap();
        Method methods[] = annotation.annotationType().getDeclaredMethods();
        for (int j = 0; j < methods.length; j++) {
            Method method = methods[j];
            if (method.getParameterTypes().length != 0 || method.getReturnType() == Void.TYPE)
                continue;
            try {
                attrs.put(method.getName(), method.invoke(annotation, new Object[0]));
            } catch (Exception ex) {
                throw new IllegalStateException("Could not obtain annotation attribute values", ex);
            }
        }

        return attrs;
    }


    /**
     * 获取对象中的注解
     *
     * @param target
     *            目标对象Object
     * @param annotationClass
     *            注解
     *
     * @return Object
     */
    public static <T> T getAnnotation(Object target, Class annotationClass) {
        Assert.notNull(target, "target不能为空");
        return (T) getAnnotation(target.getClass(), annotationClass);
    }

    /**
     * 获取对象中的注解
     *
     * @param targetClass
     *            目标对象Class
     * @param annotationClass
     *            注解类型Class
     *
     * @return Object
     */
    public static <T extends Annotation> T getAnnotation(Class targetClass,
                                                         Class annotationClass) {
        Assert.notNull(targetClass);
        Assert.notNull(annotationClass);

        if (targetClass.isAnnotationPresent(annotationClass)) {
            return (T) targetClass.getAnnotation(annotationClass);
        }

        return null;
    }

    /**
     * 获取Object对象中所有annotationClass类型的注解
     *
     * @param target
     *            目标对象Object
     * @param annotationClass
     *            Annotation类型
     *
     * @return {@link Annotation}
     */
    public static <T extends Annotation> List<T> getAnnotations(Object target,
                                                                Class annotationClass) {
        Assert.notNull(target);
        return getAnnotations(ClassUtil.getClass(target), annotationClass);
    }

    /**
     *
     * 获取对象中的所有annotationClass注解
     *
     * @param targetClass
     *            目标对象Class
     * @param annotationClass
     *            注解类型Class
     *
     * @return List
     */
    public static <T extends Annotation> List<T> getAnnotations(
            Class targetClass, Class annotationClass) {
        Assert.notNull(targetClass, "targetClass不能为空");
        Assert.notNull(annotationClass, "annotationClass不能为空");

        List<T> result = new ArrayList<T>();
        Annotation annotation = targetClass.getAnnotation(annotationClass);
        if (annotation != null) {
            result.add((T) annotation);
        }
        Constructor[] constructors = targetClass.getDeclaredConstructors();
        // 获取构造方法里的注解
        com.bolt.common.collection.CollectionUtil.addAll(result,	getAnnotations(constructors, annotationClass));

        Field[] fields = targetClass.getDeclaredFields();
        // 获取字段中的注解
        com.bolt.common.collection.CollectionUtil.addAll(result, getAnnotations(fields, annotationClass));

        Method[] methods = targetClass.getDeclaredMethods();
        // 获取方法中的注解
        com.bolt.common.collection.CollectionUtil.addAll(result, getAnnotations(methods, annotationClass) );

        for (Class<?> superClass = targetClass.getSuperclass(); superClass != null; superClass = superClass
                .getSuperclass()) {
            List<T> temp = getAnnotations(superClass, annotationClass);
            if (com.bolt.common.collection.CollectionUtil.isNotEmpty(temp)) {
                com.bolt.common.collection.CollectionUtil.addAll(result, temp.iterator());
            }
        }

        return result;
    }

    /**
     * 获取field的annotationClass注解
     *
     * @param field
     *            field对象
     * @param annotationClass
     *            annotationClass注解
     *
     * @return {@link Annotation}
     */
    public static <T extends Annotation> T getAnnotation(Field field,
                                                         Class annotationClass) {

        Assert.notNull(field, "field不能为空");
        Assert.notNull(annotationClass, "annotationClass不能为空");

        field.setAccessible(true);
        if (field.isAnnotationPresent(annotationClass)) {
            return (T) field.getAnnotation(annotationClass);
        }
        return null;
    }

    /**
     * 获取field数组中匹配的annotationClass注解
     *
     * @param fields
     *            field对象数组
     * @param annotationClass
     *            annotationClass注解
     *
     * @return List
     */
    public static <T extends Annotation> List<T> getAnnotations(Field[] fields,
                                                                Class annotationClass) {

        if (ArrayUtil.isEmpty(fields)) {
            return null;
        }

        List<T> result = new ArrayList<T>();

        for (Field field : fields) {
            field.setAccessible(true);
            Annotation annotation = getAnnotation(field, annotationClass);
            if (annotation != null) {
                result.add((T) annotation);
            }
        }

        return result;
    }

    /**
     * 获取method的annotationClass注解
     *
     * @param method
     *            method对象
     * @param annotationClass
     *            annotationClass注解
     *
     * @return {@link Annotation}
     */
    public static <T extends Annotation> T getAnnotation(Method method,
                                                         Class annotationClass) {

        Assert.notNull(method, "method不能为空");
        Assert.notNull(annotationClass, "annotationClass不能为空");

        method.setAccessible(true);
        if (method.isAnnotationPresent(annotationClass)) {
            return (T) method.getAnnotation(annotationClass);
        }
        return null;
    }

    /**
     * 获取method数组中匹配的annotationClass注解
     *
     * @param methods
     *            method对象数组
     * @param annotationClass
     *            annotationClass注解
     *
     * @return List
     */
    public static <T extends Annotation> List<T> getAnnotations(
            Method[] methods, Class annotationClass) {

        if (ArrayUtil.isEmpty(methods)) {
            return null;
        }

        List<T> result = new ArrayList<T>();

        for (Method method : methods) {

            Annotation annotation = getAnnotation(method, annotationClass);
            if (annotation != null) {
                result.add((T) annotation);
            }
        }

        return result;
    }

    /**
     * 获取constructor的annotationClass注解
     *
     * @param constructor
     *            constructor对象
     * @param annotationClass
     *            annotationClass注解
     *
     * @return {@link Annotation}
     */
    public static <T extends Annotation> T getAnnotation(
            Constructor constructor, Class annotationClass) {

        Assert.notNull(constructor, "constructor不能为空");
        Assert.notNull(annotationClass, "annotationClass不能为空");

        constructor.setAccessible(true);

        if (constructor.isAnnotationPresent(annotationClass)) {
            return (T) constructor.getAnnotation(annotationClass);
        }

        return null;
    }

    /**
     * 获取constructors数组中匹配的annotationClass注解
     *
     * @param constructors
     *            constructor对象数组
     * @param annotationClass
     *            annotationClass注解
     *
     * @return List
     */
    public static <T extends Annotation> List<T> getAnnotations(
            Constructor[] constructors, Class annotationClass) {

        if (ArrayUtil.isEmpty(constructors)) {
            return null;
        }

        List<T> result = new ArrayList<T>();

        for (Constructor constructor : constructors) {
            Annotation annotation = getAnnotation(constructor, annotationClass);
            if (annotation != null) {
                result.add((T) annotation);
            }
        }

        return result;
    }





}
